﻿using DFS = DocumentFormat.OpenXml.Lite.Core.DocumentFS;
using DocumentFormat.OpenXml.Lite.Helper;
using DocumentFormat.OpenXml.Lite.Core.Interfaces;
using DocumentFormat.OpenXml.Lite.Core.Office;
using DocumentFormat.OpenXml.Lite.DrawingML.Text;
using DocumentFormat.OpenXml.Lite.DrawingML.Text.Properties;
using DocumentFormat.OpenXml.Lite.PresentationML.Presentation.PresentationElements;
using DocumentFormat.OpenXml.Lite.Xml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using IO = System.IO;
using DocumentFormat.OpenXml.Lite.Core.Relationship;
using DocumentFormat.OpenXml.Lite.DrawingML.Style;

namespace DocumentFormat.OpenXml.Lite.PresentationML.Presentation.Base
{
    [XmlRoot("presentation", Namespace = Constants.Presentation)]
    public class Presentation : XmlSerializable, ISerializable
    {
        [XmlIgnore]
        public Guid Id { get { return manager.Id; } }

        [XmlIgnore]
        public AppProperties AppProperties
        {
            get { return manager.AppProperties; }
            set { manager.AppProperties = value; }
        }

        [XmlIgnore]
        public CoreProperties CoreProperties
        {
            get { return manager.CoreProperties; }
            set { manager.CoreProperties = value; }
        }

        [XmlIgnore]
        public List<Slide> Slides 
        {
            get
            {
                List<Slide> slides = new List<Slide>();
                foreach (var id in SlideIds)
                {
                    slides.Add(manager.FindElementFor<Slide>(this, id.RelationshipId));
                }
                return slides;
            }
        }

        [XmlIgnore]
        public List<SlideMaster> SlideMasters
        {
            get
            {
                List<SlideMaster> masters = new List<SlideMaster>();
                foreach (var id in MasterIds)
                {
                    masters.Add(manager.FindElementFor<SlideMaster>(this, id.RelationshipId));
                }
                return masters;
            }
        }

        [XmlIgnore]
        private PresentationDocument manager;

        [XmlNamespaceDeclarations]
        public XmlSerializerNamespaces Namespaces { get; set; }

        #region Attributes

        [Optional]
        [XmlAttribute("serverZoom", Form = XmlSchemaForm.Qualified)]
        public Nullable<int> ServerZoom { get; set; }

        [Optional]
        [XmlAttribute("firstSlideNum", Form = XmlSchemaForm.Qualified)]
        public Nullable<int> FirstSlideNumber { get; set; }

        [Optional]
        [XmlAttribute("showSpecialPlsOnTitleSld", Form = XmlSchemaForm.Qualified)]
        public Nullable<bool> ShowHeaderAndFooterPlaceholdersOnTitles { get; set; }

        [Optional]
        [XmlAttribute("rtl", Form = XmlSchemaForm.Qualified)]
        public Nullable<bool> RightToLeftViews { get; set; }

        [Optional]
        [XmlAttribute("removePersonalInfoOnSave", Form = XmlSchemaForm.Qualified)]
        public Nullable<bool> RemovePersonalInfoOnSave { get; set; }

        [Optional]
        [XmlAttribute("compatMode", Form = XmlSchemaForm.Qualified)]
        public Nullable<bool> CompatibilityMode { get; set; }

        [Optional]
        [XmlAttribute("strictFirstAndLastChars", Form = XmlSchemaForm.Qualified)]
        public Nullable<bool> StrictFirstAndLastChars { get; set; }

        [Optional]
        [XmlAttribute("embedTrueTypeFonts", Form = XmlSchemaForm.Qualified)]
        public Nullable<bool> EmbedTrueTypeFonts { get; set; }

        [Optional]
        [XmlAttribute("saveSubsetFonts", Form = XmlSchemaForm.Qualified)]
        public Nullable<bool> SaveSubsetFonts { get; set; }

        [Optional]
        [XmlAttribute("autoCompressPictures", Form = XmlSchemaForm.Qualified)]
        public Nullable<bool> AutoCompressPictures { get; set; }

        [Optional]
        [XmlAttribute("bookmarkIdSeed", Form = XmlSchemaForm.Qualified)]
        public Nullable<uint> BookmarkIdSeed { get; set; }

        #endregion

        #region Elements

        [Optional]
        [XmlArray("sldMasterIdLst", Namespace = Constants.Presentation)]
        [XmlArrayItem("sldMasterId", Namespace = Constants.Presentation)]
        public XmlList<SimpleIdElement> MasterIds { get; set; }

        [Optional]
        [XmlArray("notesMasterIdLst", Namespace = Constants.Presentation)]
        [XmlArrayItem("notesMasterId", Namespace = Constants.Presentation)]
        public XmlList<RelationshipElement> NotesMasterIds { get; set; }

        [Optional]
        [XmlArray("handoutMasterIdLst", Namespace = Constants.Presentation)]
        [XmlArrayItem("handoutMasterId", Namespace = Constants.Presentation)]
        public XmlList<RelationshipElement> HandoutMasterIds { get; set; }

        [Optional]
        [XmlArray("sldIdLst", Namespace = Constants.Presentation)]
        [XmlArrayItem("sldId", Namespace = Constants.Presentation)]
        public XmlList<SimpleIdElement> SlideIds { get; set; }

        [Optional]
        [XmlElement("sldSz", Namespace = Constants.Presentation)]
        public SlideSize SlideSize { get; set; }

        [XmlElement("notesSz", Namespace = Constants.Presentation)]
        public NoteSize NoteSize { get; set; }

        [Optional]
        [XmlElement("smartTags", Namespace = Constants.Presentation)]
        public RelationshipElement SmartTags { get; set; }

        [Optional]
        [XmlArray("embeddedFontLst", Namespace = Constants.Presentation)]
        [XmlArrayItem("embeddedFont", Namespace = Constants.Presentation)]
        public XmlList<EmbeddedFont> EmbeddedFonts { get; set; }

        [Optional]
        [XmlElement("photoAlbum", Namespace = Constants.Presentation)]
        public PhotoAlbum PhotoAlbum { get; set; }

        [Optional]
        [XmlElement("custDataLst", Namespace = Constants.Presentation)]
        public CustomerDataList CustomerDataList { get; set; }

        [Optional]
        [XmlElement("kinsoku", Namespace = Constants.Presentation)]
        public Kinsoku Kinsoku { get; set; }

        [Optional]
        [XmlElement("defaultTextStyle", Namespace = Constants.Presentation)]
        public TextListStyles DefaultTextStyle { get; set; }

        [Optional]
        [XmlElement("modifyVerifier", Namespace = Constants.Presentation)]
        public ModifyVerifier ModifyVerifier { get; set; }

        #endregion

        private Presentation()
        {
            Namespaces = new XmlSerializerNamespaces();
            Namespaces.Add("p", Constants.Presentation);
            Namespaces.Add("r", Constants.Relationship);
            Namespaces.Add("a", Constants.Graphic);

            MasterIds = new XmlList<SimpleIdElement>();
            NotesMasterIds = new XmlList<RelationshipElement>();
            HandoutMasterIds = new XmlList<RelationshipElement>();
            SlideIds = new XmlList<SimpleIdElement>();
        }

        internal Presentation(PresentationDocument parent)
            : this()
        {
            this.manager = parent;
        }

        #region ISerializable members

        public byte[] Serialize()
        {
            XmlSerializer serializer = new XmlSerializer(this.GetType());
            using (var stream = new IO.MemoryStream())
            {
                using (var writer = new IO.StreamWriter(stream, Encoding.UTF8))
                {
                    using (var xwriter = XmlWriter.Create(writer))
                    {
                        xwriter.WriteStartElement("p", "presentation", Constants.Presentation);
                        foreach (var ns in Namespaces.ToArray())
                        {
                            if (ns.Name != "")
                            {
                                xwriter.WriteAttributeString("xmlns", ns.Name, null, ns.Namespace);
                            }
                        }
                        this.WriteXml(xwriter);
                    }
                    return stream.ToArray();
                }
            }
        }

        public void Deserialize(System.IO.Stream content)
        {
            using (var reader = XmlReader.Create(content))
            {
                reader.ReadToDescendant("presentation", Constants.Presentation);
                for (int i = 0; i < reader.AttributeCount; ++i)
                {
                    string s = reader.GetAttribute(i);
                    if (Constants.GetDefaultPrefix(s) != string.Empty)
                    {
                        Namespaces.Add(Constants.GetDefaultPrefix(s), s);
                    }
                }
                this.ReadXml(reader);
            }
        }

        #endregion
        
        #region Public methods

        public void Save(string path)
        {
            DocumentManager.Instance.SaveDocument(manager.Id, path);
        }

        public void Save(System.IO.Stream stream)
        {
            DocumentManager.Instance.SaveDocument(manager.Id, stream);
        }

        public SlideMaster CreateSlideMaster()
        {
            string relId = string.Empty;
            SlideMaster master = manager.CreateSlideMaster(out relId);
            MasterIds.Add(new SimpleIdElement()
            {
                RelationshipId = relId,
                Id = (uint)(manager.NextInt()) + int.MaxValue + 1
            });
            return master;
        }

        public Slide CreateSlide(SlideLayout layout)
        {
            string relId = string.Empty;
            Slide slide = manager.CreateSlide(layout, out relId);
            SlideIds.Add(new SimpleIdElement()
            {
                RelationshipId = relId,
                Id = (uint)(manager.NextInt(256, 2147483647))
            });
            return slide;
        }

        public SlideLayout CreateLayout(SlideMaster master)
        {
            return manager.CreateLayout(master);
        }

        public Theme CreateTheme(SlideMaster master)
        {
            return manager.CreateTheme(master);
        }

        public SlideLayout FindLayoutForSlide(Slide slide)
        {
            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(manager.Id);
            DFS.File slideFile = sandbox.GetFilesRecursive().FirstOrDefault(f => f.Object == slide);
            DFS.File relFile = sandbox.GetFile(slideFile.Parent.GetXmlPath() + "_rels/" + slideFile.Name + ".rels");
            string relPath = (relFile.Object as RelationshipManager).RelationshipTags
                .FirstOrDefault(r => r.Type == RelationshipURI.SlideLayout.GetDescription()).Target;
            return sandbox.GetFile(DFS.Directory.Combine(slideFile.Parent, relPath)).Object as SlideLayout;
        }

        public List<Theme> FindThemes()
        {
            List<Theme> themes = new List<Theme>();
            DFS.Directory sandbox = DFS.DocumentFS.GetSandbox(manager.Id);
            themes.AddRange(sandbox.GetDirectory("ppt/theme").GetFiles().Select(f => f.Object as Theme));
            return themes;
        }

        #endregion
    }
}
